#include "h3d.h"
#include "../core/Logger.h"
#include "../core/LuaUtils.h"
#include "../core/Sheet.h"
#include "../core/Filesystem.h"
#include "../core/ObjectFactory.h"
#include "Horde3DUtils.h"
#include "h3dLuaBindings.h"
#include "SDL.h"
#include "SDL_opengl.h"

RegisterCppObj<Horde3d> g_register_horde3d_object;

int returnThreePlusX(int x)
{
	return x + 3;
}

Horde3d::Horde3d()
{
	// nothing special to do
	_autoUpdating = false;
	_autoCam = 0;
	_hasBeenInitted = false;
	_screenW = -1;
	_screenH = -1;
	_bufferInterval = 0;
	_bframe = 0;
}

// Return the c++ type id
int Horde3d::getCppTypeId()
{
	return HORDE3D_OBJ_ID;
}

// Returns whether this can be cast to a given type
bool Horde3d::isType(int id)
{
	return (id == getCppTypeId());
}

// Return the c++ type as a string 
std::string Horde3d::getCppTypeName()
{
	return "Horde3d";
}

// returns a description of this thing
std::string Horde3d::getDescription()
{
	return getName() + ": the Horde3d interface object (all static funcs)";
}

// create another one of these
AbstractObject* Horde3d::newInstance()
{
	return new Horde3d;
}

// init & update
void Horde3d::init()
{
	LOG(logINFO) << "Horde3d cpp obj initting (note: still need to call h3dinit)";
}

void Horde3d::update(double dt)
{
	if(_autoUpdating)
	{
		if(!_hasBeenInitted)
		{
			LOG(logERROR) << "Tried to auto-update graphics without h3d having been initted! Aborting auto update.";
			_autoUpdating = false;
		}

		//LOG(logINFO) << "UPDATE!";
		glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

		// Render scene
		h3dRender(_autoCam);

		// Finish rendering of frame
		h3dFinalizeFrame();

		// try to undo whatever horde3d did?
		//glMatrixMode(GL_MODELVIEW);
		//glLoadIdentity();

		// swap buffers? This may not be necessary.
		if(_bufferInterval == 1 || _bufferInterval == 0)
			SDL_GL_SwapBuffers();
		else if(_bufferInterval > 1)
		{
			_bframe = (_bframe + 1) % _bufferInterval;
			if(_bframe == 0)
				SDL_GL_SwapBuffers();
		}
	}
}

// push its static functions into the given lua table
void Horde3d::pushStaticFuncs(lua_State* L)
{
	// push superclass static functions...
	AbstractObject::pushStaticFuncs(L);

	// just push these static funcs...
	H3DLuaBindings::pushStaticFuncs(L);
	
	// push some custom ones
	static const struct luaL_Reg mylib [] = {
		{"LoadResourcesFromDisk", li_loadResourcesFromDisk},
		{"SetNodeTransformQuaternion", li_setNodeTransformQuaternion},
		{"GetOption", li_getOption},
		{"SetOption", li_setOption},
		{"GetStat", li_getStat},
		{"ResetVideoMode", li_resetVideoMode},
		{"InitGraphicsMinimal", li_initGraphicsMinimal},
		{"InitGraphics", li_initGraphics},
		{"EnableAutoUpdate", li_enableAutoUpdate},
		{"DisableAutoUpdate", li_disableAutoUpdate},
		{"GetScreenshot", li_getScreenshot},
		{"GetBBScreenshot", li_getBBScreenshot},
		{"CreateImage", li_createImage},
		{"AsInt", li_asInt},
		{"AsRes", li_asRes},
		{"AsNode", li_asNode},
		{"GetTextureAsImage", li_getTextureAsImage},
		{"SetTextureFromImage", li_setTextureFromImage},
		{"GetTextureResFromMaterial", li_getTextureResFromMaterial},
		{"SetTextureResForMaterial", li_setTextureResForMaterial},
		{"SetSwapBufferInterval", li_setSwapBufferInterval},
		{"ForceBufferSwap", li_forceBufferSwap},
		{"DrawAAROIOverlay", li_drawAAROIOverlay},
		{"GetNodeTransMats", li_getNodeTransMats},
		{NULL, NULL}  /* sentinel */
    };
/*
		static int li_getTextureAsImage(lua_State* L);
	static int li_setTextureFromImage(lua_State* L);
	static int li_getTextureResFromMaterial(lua_State* L);*/
	
    // dump them into said table
    luaL_register(L, NULL, mylib);
}

Horde3d::~Horde3d()
{
	// nothing to do (maybe release h3d? but what if we have multiple sheets that are all doing graphics stuff...)
}

void Horde3d::resetVideoMode(int desired_w, int desired_h, bool fullscreen)
{
	// Initialize SDL if necessary
	if (SDL_Init(SDL_INIT_EVERYTHING) < 0)
	{
		LOG(logERROR) << "SDL_Init: " << SDL_GetError();
		return;
	}

	// SDL/OpenGL initialization (doublebuffered, depthbuffered, 32 bit color)
	SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);

	SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 16);

	SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8);
	SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8);
	SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8);
	SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 8);

	SDL_Surface* drawContext;
	Uint32 flags;

	if(fullscreen)
		flags = SDL_OPENGL | SDL_HWSURFACE | SDL_FULLSCREEN;
	else
		flags = SDL_OPENGL | SDL_HWSURFACE;

	drawContext = SDL_SetVideoMode(desired_w, desired_h, 0, flags);
}

void Horde3d::initGraphicsMinimal(int desired_w, int desired_h, bool fullscreen)
{
	resetVideoMode(desired_w, desired_h, fullscreen);

	// Initialize engine
	if( !h3dInit() )
	{       
		LOG(logERROR) << "Horde3D init error (see engine log for details)";
		h3dutDumpMessages();
		return;
	}

	_hasBeenInitted = true;
	_screenW = desired_w;
	_screenH = desired_h;
}

void Horde3d::initGraphics(int desired_w, int desired_h, bool fullscreen)
{
	initGraphicsMinimal(desired_w, desired_h, fullscreen);

	LOG(logDEBUG) << "Setting up basic Horde3d options";

	// Set options
	h3dSetOption( H3DOptions::LoadTextures, 1 );
	h3dSetOption( H3DOptions::TexCompression, 0 );
	h3dSetOption( H3DOptions::MaxAnisotropy, 4 );
	h3dSetOption( H3DOptions::ShadowMapSize, 2048 );
	h3dSetOption( H3DOptions::FastAnimation, 1 );

	// Setup viewport (duh)
	h3dSetupViewport( 0, 0, desired_w, desired_h, true );

	h3dutDumpMessages();
	LOG(logDEBUG) << "Done with graphics init";
}

void Horde3d::enableAutoUpdate(H3DNode updateCam)
{
	if(updateCam != 0)
	{
		_autoCam = updateCam;
		_autoUpdating = true;
	}
	else
	{
		LOG(logWARNING) << "Tried to enable auto update with cam zero; stopping auto updating.";
		_autoCam = 0;
		_autoUpdating = false;
	}
}

void Horde3d::disableAutoUpdate()
{
	_autoUpdating = false;
}

void Horde3d::setNodeTransformQuaternion(H3DNode n, float tx, float ty, float tz, float rx, float ry, float rz, float rw, float sx, float sy, float sz)
{
	float x2 = rx * rx;
	float y2 = ry * ry;
	float z2 = rz * rz;
	float xy = rx * ry;
	float xz = rx * rz;
	float yz = ry * rz;
	float wx = rw * rx;
	float wy = rw * ry;
	float wz = rw * rz;

	float x[16] = {sx*(1.0f - 2.0f * (y2 + z2)), sx*(2.0f * (xy + wz)), sx*(2.0f * (xz - wy)), 0.0f,
		sy*(2.0f * (xy - wz)), sy*(1.0f - 2.0f * (x2 + z2)), sy*(2.0f * (yz + wx)), 0.0f,
		sz*(2.0f * (xz + wy)), sz*(2.0f * (yz - wx)), sz*(1.0f - 2.0f * (x2 + y2)), 0.0f,
		tx, ty, tz, 1.0f};

	h3dSetNodeTransMat( n, x );
}

SimpleImage* Horde3d::getScreenshot(SimpleImage* dest, bool backBuffer)
{
	SimpleImage* ret = dest;
	if(ret == NULL)
	{
		ret = new SimpleImage;
	}

	if(ret->sizeX() != _screenW || ret->sizeY() != _screenH || ret->sizeC() != 3)
		ret->allocateInterleaved(_screenW, _screenH, 3);

	unsigned char* p_pixels = ret->getPtr();

	// save previous read target
	GLint prevBuffer;
	glGetIntegerv( GL_READ_BUFFER, &prevBuffer );

	// read pixels
	if(!backBuffer)
		glReadBuffer( GL_FRONT );
	else
		glReadBuffer( GL_BACK );
	glReadPixels( 0, 0, _screenW, _screenH, GL_RGB, GL_UNSIGNED_BYTE, p_pixels );

	// restore previous read target
	glReadBuffer( prevBuffer );
	return ret;
}

SimpleImage* Horde3d::getTextureAsImage(H3DRes texRes, SimpleImage* dest)
{
	// get size of texture image
	int width = h3dGetResParamI( texRes, H3DTexRes::ImageElem, 0, H3DTexRes::ImgWidthI );
	int height = h3dGetResParamI( texRes, H3DTexRes::ImageElem, 0, H3DTexRes::ImgHeightI );

	LOG(logDEBUG) << "gtexasimage w: " << width << ", height: " << height;

	if(dest == NULL)
		dest = new SimpleImage;

	if(dest->sizeX() != width || dest->sizeY() != height || dest->sizeC() != 4)
		dest->allocateInterleaved(width, height, 4);

	// do some resource junk...
	// first, get texture format (this only supports BGRA8)
	int texFormat = h3dGetResParamI(texRes, H3DTexRes::TextureElem, 0, H3DTexRes::TexFormatI);
	if(texFormat != H3DFormats::TEX_BGRA8)
	{
		LOG(logERROR) << "Tried to get non BGRA8 texture as image; tex format is " << texFormat;
		return dest;
	}

	// map the resource
	unsigned char* imgData = (unsigned char*)h3dMapResStream(texRes, H3DTexRes::ImageElem, 0, H3DTexRes::ImgPixelStream, true, false);
	if(imgData != NULL)
	{
		for(int i = 0; i < (width * height * 4); ++i)
			dest->rawSet(i, imgData[i]);
	}
	else
	{
		LOG(logERROR) << "Null image data!";
	}
	h3dUnmapResStream(texRes);

	return dest;
}

void Horde3d::setTextureFromImage(H3DRes texRes, SimpleImage* src)
{
	// get size of texture image
	int width = h3dGetResParamI( texRes, H3DTexRes::ImageElem, 0, H3DTexRes::ImgWidthI );
	int height = h3dGetResParamI( texRes, H3DTexRes::ImageElem, 0, H3DTexRes::ImgHeightI );

	if(src == NULL)
	{
		LOG(logERROR) << "Null image in setTextureFromImage!";
		return;
	}

	// do some resource junk...
	// first, get texture format (this only supports BGRA8)
	int texFormat = h3dGetResParamI(texRes, H3DTexRes::TextureElem, 0, H3DTexRes::TexFormatI);
	if(texFormat != H3DFormats::TEX_BGRA8)
	{
		LOG(logERROR) << "Tried to set non BGRA8 texture from image; tex format is " << texFormat;
		return;
	}

	// map the resource
	unsigned char* imgData = (unsigned char*)h3dMapResStream(texRes, H3DTexRes::ImageElem, 0, H3DTexRes::ImgPixelStream, false, true);
	if(imgData != NULL)
	{
		if(width == src->sizeX() && height == src->sizeY() && src->sizeC() == 4)
		{
			// we can do this pretty simply
			unsigned char* rawSrc = src->getPtr();
			for(int i = 0; i < (width * height * 4); ++i)
				imgData[i] = rawSrc[i];
		}
		else // more expensive
		{
			LOG(logWARNING) << "Size mismatch for setting texture from image; src will be cropped or padded (also this is much slower)";
			LOG(logDEBUG) << "Src: " << src->sizeX() << "x" << src->sizeY() << "x" << src->sizeC();
			LOG(logDEBUG) << "Dest: " << width << "x" << height << "x4";
			int rp = 0;
			for(int y = 0; y < height; ++y)
			{
				for(int x = 0; x < width; ++x)
				{
					for(int c = 0; c < 4; ++c)
					{
						imgData[rp] = src->get(x, y, c);
						++rp;
					}
				}
			}
		}
	}
	else
	{
		LOG(logERROR) << "Null dest texture data!";
	}
	h3dUnmapResStream(texRes);
}

H3DRes Horde3d::getTextureResFromMaterial(H3DRes matRes, std::string& samplerName)
{
	int idx = h3dFindResElem( matRes, H3DMatRes::SamplerElem, H3DMatRes::SampNameStr, samplerName.c_str());
	int texRes = h3dGetResParamI( matRes, H3DMatRes::SamplerElem, idx, H3DMatRes::SampTexResI);
	return texRes;
}

void Horde3d::setTextureResForMaterial(H3DRes matRes, std::string& samplerName, H3DRes texRes)
{
	int idx = h3dFindResElem( matRes, H3DMatRes::SamplerElem, H3DMatRes::SampNameStr, samplerName.c_str());
	LOG(logDEBUG) << "Idx for " << samplerName << ": " << idx;
	h3dSetResParamI( matRes, H3DMatRes::SamplerElem, idx, H3DMatRes::SampTexResI, texRes);
}

void Horde3d::setSwapBufferInterval(int bufferInterval)
{
	_bufferInterval = bufferInterval;
	_bframe = 0;
}
void Horde3d::forceBufferSwap()
{
	SDL_GL_SwapBuffers();
}

int Horde3d::li_setNodeTransformQuaternion(lua_State* L)
{
	//ignore first argument
	H3DNode a1 = unboxH3DNode(L, 2);
	float a2 = (float)lua_tonumber(L, 3);
	float a3 = (float)lua_tonumber(L, 4);
	float a4 = (float)lua_tonumber(L, 5);
	float a5 = (float)lua_tonumber(L, 6);
	float a6 = (float)lua_tonumber(L, 7);
	float a7 = (float)lua_tonumber(L, 8);
	float a8 = (float)lua_tonumber(L, 9);
	float a9 = (float)lua_tonumber(L, 10);
	float a10 = (float)lua_tonumber(L, 11);
	float a11 = (float)lua_tonumber(L, 12);

	setNodeTransformQuaternion(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11);
	return 0;
}


int Horde3d::li_loadResourcesFromDisk(lua_State* L)
{
	Horde3d* self = (Horde3d*)typeCheckUnboxAO(L, 1, "Horde3d");
	if(self == NULL)
	{
		LOG(logERROR) << "Horde3d.LoadResourceFromDist li error: self is null";
		return 0;
	}
	std::string sp = self->getSheet()->fs()->toAbsolutePath(self->getSheet()->fs()->getLocalPath());
	LOG(logINFO) << "Trying to load H3d resources from absolute working path: " << sp;
	
	h3dutLoadResourcesFromDisk(sp.c_str());
	return 0;
}

int Horde3d::li_getOption(lua_State* L)
{
	H3DOptions::List param = (H3DOptions::List) lua_tointeger(L,2);
	float ret = h3dGetOption(param);
	lua_pushnumber(L, ret);
	return 1;
}

int Horde3d::li_setOption(lua_State* L)
{
	H3DOptions::List param = (H3DOptions::List) lua_tointeger(L,2);
	float val = (float)lua_tonumber(L,3);
	bool ret = h3dSetOption(param, val);
	lua_pushboolean(L, ret);
	return 1;
}

int Horde3d::li_getStat(lua_State* L)
{
	H3DStats::List param = (H3DStats::List) lua_tointeger(L,2);
	bool reset = (bool)(lua_toboolean(L, 3) > 0);
	float res = h3dGetStat(param, reset);
	lua_pushnumber(L, res);
	return 1;
}

int Horde3d::li_resetVideoMode(lua_State* L)
{
	Horde3d* op = (Horde3d*)typeCheckUnboxAO(L,1,"Horde3d");
	if(op == NULL){
		LOG(logERROR) << "Null self!";
	return 0;}
	int a1 = (int)lua_tointeger(L, 2);
	int a2 = (int)lua_tointeger(L, 3);
	bool a3 = (bool)(lua_toboolean(L, 4) > 0);
	op->resetVideoMode(a1, a2, a3);
	
	return 0;
}


int Horde3d::li_initGraphicsMinimal(lua_State* L)
{
	Horde3d* op = (Horde3d*)typeCheckUnboxAO(L,1,"Horde3d");
	if(op == NULL){
		LOG(logERROR) << "Null self!";
	return 0;}
	int a1 = (int)lua_tointeger(L, 2);
	int a2 = (int)lua_tointeger(L, 3);
	bool a3 = (bool)(lua_toboolean(L, 4) > 0);
	op->initGraphicsMinimal(a1, a2, a3);
	
	return 0;
}


int Horde3d::li_initGraphics(lua_State* L)
{
	Horde3d* op = (Horde3d*)typeCheckUnboxAO(L,1,"Horde3d");
	if(op == NULL){
		LOG(logERROR) << "Null self!";
	return 0;}
	int a1 = (int)lua_tointeger(L, 2);
	int a2 = (int)lua_tointeger(L, 3);
	bool a3 = (bool)(lua_toboolean(L, 4) > 0);
	op->initGraphics(a1, a2, a3);
	
	return 0;
}


int Horde3d::li_enableAutoUpdate(lua_State* L)
{
	Horde3d* op = (Horde3d*)typeCheckUnboxAO(L,1,"Horde3d");
	if(op == NULL){
		LOG(logERROR) << "Null self!";
	return 0;}
	H3DNode a1 = unboxH3DNode(L, 2);
	op->enableAutoUpdate(a1);
	
	return 0;
}


int Horde3d::li_disableAutoUpdate(lua_State* L)
{
	Horde3d* op = (Horde3d*)typeCheckUnboxAO(L,1,"Horde3d");
	if(op == NULL){
		LOG(logERROR) << "Null self!";
	return 0;}
	op->disableAutoUpdate();
	
	return 0;
}

int Horde3d::li_getScreenshot(lua_State* L)
{
	//LOG(logDEBUG) << "Getting screenshot...";
	Horde3d* op = (Horde3d*)typeCheckUnboxAO(L,1,"Horde3d");
	if(op == NULL){
		LOG(logERROR) << "Null self!";
	return 0;}
	SimpleImage* arg1;
	if(lua_isnil(L, 2))
		arg1 = NULL;
	else
		arg1 = pop_custom<SimpleImage>(L, 2, "SimpleImage");
	//LOG(logDEBUG) << "Woo?";
	SimpleImage* ret = op->getScreenshot(arg1);
	/*if(ret)
		LOG(logDEBUG) << "Successfully grabbed frame; w= " << ret->sizeX() << ", h= " << ret->sizeY();
	else
		LOG(logDEBUG) << "Null frame captured.";*/
	if(arg1 == ret)
		lua_pushvalue(L, 2);
	else
		push_custom<SimpleImage>(L, ret, "SimpleImage");
	return 1;
}

int Horde3d::li_getBBScreenshot(lua_State* L)
{
	Horde3d* op = (Horde3d*)typeCheckUnboxAO(L,1,"Horde3d");
	if(op == NULL){
		LOG(logERROR) << "Null self!";
	return 0;}
	SimpleImage* arg1;
	if(lua_isnil(L, 2))
		arg1 = NULL;
	else
		arg1 = pop_custom<SimpleImage>(L, 2, "SimpleImage");
	SimpleImage* ret = op->getScreenshot(arg1, true);
	if(arg1 == ret)
		lua_pushvalue(L, 2);
	else
		push_custom<SimpleImage>(L, ret, "SimpleImage");
	return 1;
}

int Horde3d::li_createImage(lua_State* L)
{
	SimpleImage* ret = new SimpleImage;
	push_custom<SimpleImage>(L, ret, "SimpleImage");
	return 1;
}

int Horde3d::li_getTextureAsImage(lua_State* L)
{
	Horde3d* op = (Horde3d*)typeCheckUnboxAO(L,1,"Horde3d");
	if(op == NULL){
		LOG(logERROR) << "Null self!";
		return 0;}
	H3DRes arg1 = unboxH3DRes(L, 2);
	LOG(logDEBUG) << "gtexasimage arg1: " << arg1;
	SimpleImage* arg2;
	if(lua_isnil(L, 3))
		arg2 = NULL;
	else
		arg2 = pop_custom<SimpleImage>(L, 3, "SimpleImage");
	//LOG(logDEBUG) << "Woo?";
	SimpleImage* ret = op->getTextureAsImage(arg1, arg2);
	/*if(ret)
	LOG(logDEBUG) << "Successfully grabbed frame; w= " << ret->sizeX() << ", h= " << ret->sizeY();
	else
	LOG(logDEBUG) << "Null frame captured.";*/
	if(arg2 == ret)
		lua_pushvalue(L, 3);
	else
		push_custom<SimpleImage>(L, ret, "SimpleImage");
	return 1;
}

int Horde3d::li_setTextureFromImage(lua_State* L)
{
	Horde3d* op = (Horde3d*)typeCheckUnboxAO(L,1,"Horde3d");
	if(op == NULL){
		LOG(logERROR) << "Null self!";
		return 0;}
	H3DRes arg1 = unboxH3DRes(L, 2);
	SimpleImage* arg2;
	if(lua_isnil(L, 3))
		arg2 = NULL;
	else
		arg2 = pop_custom<SimpleImage>(L, 3, "SimpleImage");
	op->setTextureFromImage(arg1, arg2);
	return 0;
}

int Horde3d::li_getTextureResFromMaterial(lua_State* L)
{
	Horde3d* op = (Horde3d*)typeCheckUnboxAO(L,1,"Horde3d");
	if(op == NULL){
		LOG(logERROR) << "Null self!";
		return 0;}
	H3DRes arg1 = unboxH3DRes(L, 2);
	std::string arg2 = getSafeLuaString(L, 3);
	H3DRes ret = op->getTextureResFromMaterial(arg1, arg2);
	pushH3DRes(L, ret);
	return 1;
}

int Horde3d::li_setTextureResForMaterial(lua_State* L)
{
	Horde3d* op = (Horde3d*)typeCheckUnboxAO(L,1,"Horde3d");
	if(op == NULL){
		LOG(logERROR) << "Null self!";
		return 0;}
	H3DRes arg1 = unboxH3DRes(L, 2);
	std::string arg2 = getSafeLuaString(L, 3);
	H3DRes arg3 = unboxH3DRes(L, 4);
	op->setTextureResForMaterial(arg1, arg2, arg3);
	return 0;
}

int Horde3d::li_asInt(lua_State* L)
{
	if(lua_isnumber(L, 2))
	{
		lua_pushvalue(L, 2);
		return 1;
	}
	else
	{
		H3DRes arg1 = unboxH3DRes(L, 2);
		lua_pushinteger(L, arg1);
		return 1;
	}
}

int Horde3d::li_asRes(lua_State* L)
{
	if(lua_isnumber(L, 2))
	{
		int arg1 = lua_tointeger(L, 2);
		pushH3DRes(L, arg1);
		return 1;
	}
	else if(lua_isuserdata(L, 2))
	{
		lua_pushvalue(L, 2);
		return 1;
	}
	else
	{
		return 0;
	}
}

int Horde3d::li_asNode(lua_State* L)
{
	if(lua_isnumber(L, 2))
	{
		int arg1 = lua_tointeger(L, 2);
		pushH3DNode(L, arg1);
		return 1;
	}
	else if(lua_isuserdata(L, 2))
	{
		lua_pushvalue(L, 2);
		return 1;
	}
	else
	{
		return 0;
	}
}

int Horde3d::li_setSwapBufferInterval(lua_State* L)
{
	Horde3d* op = (Horde3d*)typeCheckUnboxAO(L,1,"Horde3d");
	if(op == NULL){
		LOG(logERROR) << "Null self!";
		return 0;}
	int interval = lua_tointeger(L, 2);
	op->setSwapBufferInterval(interval);
	return 0;
}

int Horde3d::li_forceBufferSwap(lua_State* L)
{
	Horde3d* op = (Horde3d*)typeCheckUnboxAO(L,1,"Horde3d");
	if(op == NULL){
		LOG(logERROR) << "Null self!";
		return 0;}
	op->forceBufferSwap();
	return 0;
}


// (x,y,w,h) (u1,v1,u2,v2) (rx1,ry1,rx2,ry2) (r,g,b,a) (mat)
int Horde3d::li_drawAAROIOverlay(lua_State* L)
{
	float arr[16];
	//float* arr = new float[16];

	// upper left corner
	float x1 = (float)lua_tonumber(L, 2);
	float y1 = (float)lua_tonumber(L, 3);
	// w/h
	float w = (float)lua_tonumber(L, 4);
	float h = (float)lua_tonumber(L, 5);
	// uv of upper left
	float u1 = (float)lua_tonumber(L, 6);
	float v1 = (float)lua_tonumber(L, 7);
	// uv of lower right
	float u2 = (float)lua_tonumber(L, 8);
	float v2 = (float)lua_tonumber(L, 9);
	// ROI upper left
	float rx1 = (float)lua_tonumber(L, 10);
	float ry1 = (float)lua_tonumber(L, 11);
	// ROI lower right
	float rx2 = (float)lua_tonumber(L, 12);
	float ry2 = (float)lua_tonumber(L, 13);

	//LOG(logDEBUG) << "A: " << u1 << ", " << v1 << ", " << u2 << ", " << v2;

	// do clipping
	float x2 = x1 + w;
	float y2 = y1 + h;

	//LOG(logDEBUG) << "x1: " << x1 << ", rx1: " << rx1;
	//LOG(logDEBUG) << "x2: " << x2 << ", rx2: " << rx2;
	//LOG(logDEBUG) << "y1: " << y1 << ", ry1: " << ry1;
	//LOG(logDEBUG) << "y2: " << y2 << ", ry2: " << ry2;

	
	float cx1;
	float cu1;
	if(x1 >= rx1)
	{
		cx1 = x1;
		cu1 = u1;
	}
	else
	{
		cx1 = rx1;
		cu1 = ((rx1 - x1) / w) * (u2 - u1) + u1;
	}
	float cy1;
	float cv1;
	if(y1 >= ry1)
	{
		cy1 = y1;
		cv1 = v1;
	}
	else
	{
		cy1 = ry1;
		cv1 = ((ry1 - y1) / h) * (v2 - v1) + v1;
	}
	float cx2;
	float cu2;
	if(x2 <= rx2)
	{
		cx2 = x2;
		cu2 = u2;
	}
	else
	{
		cx2 = rx2;
		cu2 = ((rx2 - x1) / w) * (u2 - u1) + u1;
	}
	float cy2;
	float cv2;
	if(y2 <= ry2)
	{
		cy2 = y2;
		cv2 = v2;
	}
	else
	{
		cy2 = ry2;
		cv2 = ((ry2 - y1) / h) * (v2 - v1) + v1;
	}

	if(cx2 <= cx1 || cy2 <= cy1)
		return 0;

	// BL
	arr[0] = cx1;
	arr[1] = cy2;
	arr[2] = cu1;
	arr[3] = cv2;
	// BR
	arr[4] = cx2;
	arr[5] = cy2;
	arr[6] = cu2;
	arr[7] = cv2;
	// TR
	arr[8] = cx2;
	arr[9] = cy1;
	arr[10] = cu2;
	arr[11] = cv1;
	// TL
	arr[12] = cx1;
	arr[13] = cy1;
	arr[14] = cu1;
	arr[15] = cv1;

	// rgb
	float r = (float)lua_tonumber(L, 14);
	float g = (float)lua_tonumber(L, 15);
	float b = (float)lua_tonumber(L, 16);
	float a = (float)lua_tonumber(L, 17);

	//LOG(logDEBUG) << r << " " << g << " " << b << " " << a;

	//for(int i = 0; i < 16; ++i)
	//	LOG(logDEBUG) << "Arr[" << i << "] = " << arr[i];

	// material
	H3DRes res = unboxH3DRes(L, 18);
	h3dShowOverlays(arr, 4, r, g, b, a, res, 0);

	return 0;
}

int Horde3d::li_getNodeTransMats(lua_State* L)
{
	const float* relMat;
	const float* absMat;

	H3DNode n = unboxH3DNode(L, 2);

	h3dGetNodeTransMats(n, &relMat, &absMat);

	pushNumericalArray<const float>(L, relMat, 16);
	pushNumericalArray<const float>(L, absMat, 16);

	return 2;
}